#include "com_example_memloader_NativeMethod.h"

#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
#include <android/log.h>

#define  LOG_TAG "ndk_test"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

static const char* g_pstrClassPathName = "com/example/memloader";

JNINativeMethod *g_dvm_dalvik_system_DexFile = NULL;		// which in destination so file

// global  function pointer
typedef uint32_t u4;
void (*g_openDexFile)(const u4* args, union JValue* pResult);	//store the found function ptr
static const char* g_cstrDestFunc = "openDexFile";	// function name to be found
static const char* g_cstrDestFuncSig = "([B)I";		// function sig to be found

JNIEXPORT jint JNICALL thisloadDex (JNIEnv * env, jclass cls, jbyteArray bytesDex, jlong lDexLength);
// java methods ------ native methods
static  JNINativeMethod g_MyNativeMethod[] = {
		{"loadDex", "([BJ)I", (void*)thisloadDex},
	};

static int registerNativeMethod(JNIEnv* env, const char* clsname,
		JNINativeMethod *pmethods, int nMethods) {
	// 1. findclss
	jclass cls = env->FindClass(clsname);
	if (cls == NULL) {
		LOGI("registerNativeMethod : env->FindClass(clsname); Failed");
		return JNI_FALSE;
	}
	LOGI("registerNativeMethod : env->FindClass(clsname); OK");

	// 2. register
	jint nRet = env->RegisterNatives(cls, pmethods, nMethods);
	if (nRet < 0) {
		LOGI(
				"registerNativeMethod : env->RegisterNatives(cls, pmethods, nMethods); Failed");
		return JNI_FALSE;
	}
	LOGI(
			"registerNativeMethod : env->RegisterNatives(cls, pmethods, nMethods); OK");

	return JNI_TRUE;
}

static int registerMyMethods(JNIEnv *env){
	return registerNativeMethod(env, g_pstrClassPathName, g_MyNativeMethod,
			sizeof(g_MyNativeMethod) / sizeof(g_MyNativeMethod[0]));
}

/*
 * Class:     com_example_memloader_NativeMethod
 * Method:    loadDex
 * Signature: ([BJ)I
 */
JNIEXPORT jint JNICALL thisloadDex
  (JNIEnv * env, jclass cls, jbyteArray bytesDex, jlong lDexLength){
	jint  jint_result;

//	g_openDexFile(bytesDex, lDexLength);
	return jint_result;
}
static jint registerMyMethodsByJVM(JavaVM * vm){
	//1.1. get JNIEnv
	JNIEnv *env = NULL;
	if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK){
		LOGI("static jint registerMyMethodsByVM(JavaVM * vm) : vm->GetEnv ; Failed");
		return JNI_ERR;
	}
	LOGI("static jint registerMyMethodsByVM(JavaVM * vm) : vm->GetEnv ; OK");

	//1.2. register method_table
	registerMyMethods(env);
	LOGI("static jint registerMyMethodsByVM(JavaVM * vm) : registerMyMethods(env); OK");
	return JNI_OK;
}

// find function pointer from JNINativeMethod
int lookUpFunction(JNINativeMethod * jniMethodTable, const char* strDestName,
					const char* strDestMethdSig, void** ppOutFuncation ){
	int i = 0;
	while (jniMethodTable[i].name != NULL){

		LOGI("lookUpFunction: %d  %s", i, jniMethodTable[i].name);

		if (strcmp(jniMethodTable[i].name, strDestName) == 0
				&& strcmp(jniMethodTable[i].signature, strDestMethdSig) == 0){
			*ppOutFuncation = jniMethodTable[i].fnPtr;
			LOGI("lookUpFunction: have find:  %s  %s",
					jniMethodTable[i].name, jniMethodTable[i].signature);
			return JNI_TRUE;
		}

		i++;
	}

	return JNI_FALSE;
}

/* This function will be call when the library first be load.
 * You can do some init in the libray. return which version jni it support.
 */
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
	int nRet = 0;
	//1. register methods
	if (registerMyMethodsByJVM(vm) != JNI_OK){
		LOGI("JNI_OnLoad(): registerMyMethodsByJVM(vm); Failed");
	}
	LOGI("JNI_OnLoad(): registerMyMethodsByJVM(vm); OK ");

	//2. get ptrFunc : openDexFile
	//2.1 dlopen libdvm.so
	void *plibdvmso = (void*)dlopen("libdvm.so", RTLD_NOW);
	if (plibdvmso == NULL ){
		LOGI("dlopen; Failed");
		goto SAFE_EXIT;
	}
	LOGI("dlopen: OK");
	//2.2 get JNINativeMethod
	g_dvm_dalvik_system_DexFile = (JNINativeMethod *)dlsym(plibdvmso, "dvm_dalvik_system_DexFile");
	if (g_dvm_dalvik_system_DexFile == NULL){
		LOGI("dlsym(plibdvmso, \"dvm_dalvik_system_DexFile\"); Failed");
		goto SAFE_EXIT;
	}
	LOGI("dlsym(plibdvmso, \"dvm_dalvik_system_DexFile\"); OK");
	// 2.3 get g_openDexFile
	nRet =lookUpFunction(g_dvm_dalvik_system_DexFile, g_cstrDestFunc, g_cstrDestFuncSig, (void**)&g_openDexFile);
	if (nRet == JNI_FALSE){
		LOGI("lookUpFunction;  Failed");
		goto SAFE_EXIT;
	}
	LOGI("lookUpFunction;  OK");

SAFE_EXIT:
	LOGI("JNI_OnLoad---- SAFE_EXIT ");
	return 0;
}

